package javapop.framework.parser.expr;

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.Iterator;
import java.util.List;

import javapop.framework.DefaultParseResult;
import javapop.framework.ParseError;
import javapop.framework.ParseResult;

public class ExprContext {
	private List<ExprNode> output;
	private Deque<ExprNode> stack;
	private int nestLevel;
	private boolean expectExpr;
	
	public ExprContext() {
		output = new ArrayList<ExprNode>();
		stack = new ArrayDeque<ExprNode>();
		expectExpr = true;
		nestLevel = 0;
	}
	
	public boolean expectExpr() {
		return expectExpr;
	}
	
	public int getNestLevel() {
		return nestLevel;
	}
	
	public void open() {
		nestLevel++;
	}
	
	public void close() {
		nestLevel--;
	}
	
	public void expectExpr(boolean expectExpr) {
		this.expectExpr = expectExpr;
	}
	
	public void append(ExprNode node) {
		output.add(node);
	}
	
	public void push(ExprNode node) {
		stack.addLast(node);
	}
	
	public boolean hasTop() {
		return !stack.isEmpty();
	}
	
	public ExprNode pop() {
		if(stack.isEmpty()) {
			throw new IllegalStateException("Stack is empty");
		}
		return stack.removeLast();
	}
	
	@SuppressWarnings("unchecked")
	public ParseResult<? extends ExprNode> generateAST() {
		Deque<ExprNode> stack = new ArrayDeque<ExprNode>();
		for(ExprNode node : output) {
			if(node.isOperand()) {
				stack.push(node);
			} else if(node.isPrefix()) {				
				ExprNode operand = stack.pop();
				if(operand==null) {
					return (ParseResult) new ParseError("Expecting an operand for prefix operator");
				}
				node.asPrefix().setOperand(operand);
				stack.push(node);				
			} else if(node.isPostfix()) {
				ExprNode operand = stack.pop();
				if(operand==null) {
					return (ParseResult) new ParseError("Expecting an operand for postfix operator");
				}
				node.asPostfix().setOperand(operand);
				stack.push(node);
			} else if(node.isInfix()) {
				ExprNode right = stack.pop();
				if(right==null) {
					return (ParseResult) new ParseError("Expecting a right operand for infix operator");
				}
				ExprNode left = stack.pop();
				if(left==null) {
					return (ParseResult) new ParseError("Expecting a left operand for infix operator");
				}
				node.asInfix().setLeftOperand(left);
				node.asInfix().setRightOperand(right);
				stack.push(node);
			}
		}
		if(stack.size()!=1) {
			return (ParseResult) new ParseError("Expecting a well-formed expression");
		}
		ParseResult<? extends ExprNode> result = new DefaultParseResult<ExprNode>(ExprNode.class,stack.pop());
		return result;
	}
	
	@Override
	public String toString() {
		StringBuilder buf = new StringBuilder();
		buf.append("Expression context:\n");
		buf.append("Stack = [");
		Iterator<ExprNode> iter = stack.descendingIterator();
		while(iter.hasNext()) {
			buf.append(iter.next());
			buf.append(" ");
		}
		buf.append("]\n");
		buf.append("Output = [");
		for(ExprNode node : output) {
			buf.append(node);
			buf.append(" ");
		}
		buf.append("]\n");
		buf.append("Expect operand ? ");
		buf.append(expectExpr);
		return buf.toString();
	}
}
